#include "syscall.h"
typedef int bool;

#define true 1
#define false 0
#define NULL 0

#define MAX_CRNum 3 
#define MAX_ClerkNum 3
#define MAX_StockBoyNum 3
#define MAX_CustomerNum 20

#define MinPrices 1
#define PriceFactor 100

/*Environment Varible*/
int CRNum;
int ClerkNum;
int StockBoyNum;
int CustomerNum;
#define AisleNum 5
#define ItemNum 10

/*Tricks*/
/*These Varibles is used to create specific scenario for test cases*/
int ReturnChance;					/*The chance if a customer to be return customer*/
int LostChance;						/*The chance a customer fails to find an item*/
int OriginItemAmount;					/*Origin Amount of each Item*/
int MinBoughtEach;					/*Min number of items a customer can buy*/
int MaxBoughtEach;					/*Max number of items a customer can buy is [MinBoughtEach+MaxBoughtEach-1]*/
int BornLazy;						/*If a clerk will go to rest as soon as he is created?*/
/*int PriceFactor;*/					/*The Factor that affects randomly generated prices*/
/*int MinPrice;*/						/*The Mininum price can be generated*/
bool ManagerTrackLine;					/*Does Manager keep track of Line himself*/
bool CustomerBusyWaiting;
int ProgramRun;
int TotalSales;						/*record the total sales amount.*/

/*Locks*/
int initLock;
int idLock;							/*Lock to pretect id from race condition*/
int OpLock1;							/*StartLine Exclusion*/
int OpLock2;							/*StockBoy Break Exclusion*/
int OpLock3;							/*Clerk Break Exclusion*/
int OpLock4;							/*Leaving Customer Exclusion*/
int OpLock5;							/*For StockRoom Condition Check	*/
int TableLock[MAX_CRNum];				/*Acquire this Lock before handle items on table*/
int CheckQueue;							/*Acquire this lock before check Line Lengths*/
int ScreenLock[MAX_CRNum];				/*Acquire this Lock before interact with cash register machine*/
int StockRoomLock;						/*The Lock on StockRoom*/
int ServiceButtonLock;					/*The Lock to protect service button*/
int ClerkCallLock;						/*The Lock to protect clerk call flag*/
int StockBoyStatusLock;					/*The Lock to protect Stock Boy Status*/
int AisleHelpLock[AisleNum];		/*The Lock to protect AisleHelp related monitor varibles*/
int AisleLock[AisleNum];			/*Acquire this Lock before access any item on an aisle*/
int ServiceStateLock[MAX_StockBoyNum];	/*The Lock to protect help state between a stock boy and a customer*/

/*CVs*/
int StartLine;							/*the start line*/
int ClerkOnBreak;						/*The Condition that clerks on break wait on*/
int StockBoyOnBreak;					/*The Condition that stockboys on break wait on*/
int Queue[MAX_CRNum];					/*Paying Lines*/
int Screen[MAX_CRNum];					/*The cash register machine screen State Condition*/
int Table[MAX_CRNum];					/*The Cashier Table State Condition*/
int LackMoney[MAX_CRNum];				/*If a cash register lacks money to pay return customer, he waits for Manager here*/
int StockRoomCV;						/*The Condition on which Stock Boy wait for room*/
int WaitForRestockCV[ItemNum];		/*Customers waiting for item to be restocked. It is protected by AisleLock*/
int ServiceStateCV[MAX_StockBoyNum];	/*The State Condition when a stock boy is helping a customer*/
int AisleHelpCV[AisleNum];			/*The Condition a customer waits on before a stock boy comes to serve him*/
/*Synchronization Define End*/

/*Shared Varible End*/
int AisleNeedRestock;	/*record if an aisle need to be restocked*/
int ServiceButtonOn;	/*false*/
			/*If somebody pressed service button?*/
int ClerkCall;  	/*false;*/					/*If somebody ask for more clerk?*/
int ClerkCallId;					/*The Clerk who asked manager to wake another clerk*/

int QueueLength;			/*Lengths of paying Lines*/
int ClerkOnWork;			/*If a Clerk is ready for customer?*/
		
int TableArea;				/*Place to put items*/
int CashArea;				/*Place to put money*/

int data;				/*The data is the place to exchange information with cash register*/
int MoneyCollected;			/*The money each cash register earned before manager empty it*/
int TotalMoneyCollected;		/*The money each cash register ever earned*/
int MoneyLacked;			/*The money a cash register lacks to pay a return customer*/

int StockBoyInRoom;			/*Id of StockBoy currently in Stock Room*/

int StockBoyStatus;			/*Each StockBoy's status.If he is in an aisle, status is the aisle number or aisle+AisleNum; Otherwise,  -1*/
int AisleHelpLineLength;		/*The number of customer waiting for help on each aisle*/
int Booklet;				/*The booklet used by Customer and StockBoy to exchange information*/

int CustomerLeaving;			/*Total number of customers that finished shopping and left*/

int DidSomething;                       /*[MAX_StockBoyNum];*/
int Prices;
int ItemAmount;
int ItemRestockPrediction;

/*ID Assignment*/
int nextManagerID;
int nextCustomerID;
int nextClerkID;
int nextStockBoyID;
int nextCashRegisterID;

int GenRandom(int limit)
{
	return Random(limit);
}

int modifyName(char * name, int offset, int num)
{
	int i=0;
	int pos=0;
	int digit;
	if(name == NULL) return -1;
	while(name[i]!='\0' && i<offset+4){
		i++;
	}
	if(name[i]=='\0' && i < offset+4) return -1;
	pos= i-1;
	while(num!=0){
		digit=num%10;
		num = (num-digit)/10;
		name[pos]=(char)(digit+48);
		pos--;
	}
	for(;pos>=offset;pos--)
		name[pos]='0';
	return 1;
}

void initialize()
{
	char* name;
	int numbers[6];
	int i;
		
	WriteFormat("Begin to initialize!\n",22,0,0);
	nextManagerID = CreateMV("nextManagerID",13,1,0);
	WriteFormat("create one MV successfully\n",28,0,0);
	nextCustomerID = CreateMV("nextCustomerID",14,1,0);
	nextClerkID = CreateMV("nextClerkID",11,1,0);
	nextStockBoyID = CreateMV("nextSBID",14,1,0);
	nextCashRegisterID = CreateMV("nextCRID",18,1,0);

	CRNum = GetMV(CreateMV("CRNum",5,1,MAX_CRNum),0);
	ClerkNum = GetMV(CreateMV("ClerkNum",8,1,MAX_ClerkNum),0);
	StockBoyNum = GetMV(CreateMV("StockBoyNum",11,1,MAX_StockBoyNum),0);
	CustomerNum = GetMV(CreateMV("CustomerNum",11,1,MAX_CustomerNum),0);	

	OpLock1 = RemoteCreateLock("OperationLock1",15); 		
	OpLock2 = RemoteCreateLock("OperationLock2",15);
	OpLock3 = RemoteCreateLock("OperationLock3",15);
	OpLock4 = RemoteCreateLock("OperationLock4",15);
	OpLock5 = RemoteCreateLock("OperationLock5",15);
	idLock= RemoteCreateLock("IDLock",7);
	StartLine =  RemoteCreateCondition("StartLine",10);
	ClerkOnBreak =  RemoteCreateCondition("ClerkBreak",11);
	StockBoyOnBreak =  RemoteCreateCondition("StockBoyBreak",14);
	CheckQueue = RemoteCreateLock("CheckQueueLock",15);
	StockRoomLock = RemoteCreateLock("StockRoomLock",14);
	StockRoomCV = RemoteCreateCondition("StockRoomCV",12);
	ServiceButtonLock = RemoteCreateLock("ServiceButtonLock",18);
	StockBoyStatusLock = RemoteCreateLock("StockBoyStatusLock",19);
	ClerkCallLock=RemoteCreateLock("ClerkCallLock",14);

	/*MV initialize*/
	ReturnChance = CreateMV("ReturnChance",12,1,20);	/*The chance if a customer to be return customer*/
	LostChance = CreateMV("LostChance",10,1,25);		/*The chance a customer fails to find an item*/
	OriginItemAmount = CreateMV("OriginItemAmount",16,1,3);	/*Origin Amount of each Item*/
	MinBoughtEach = CreateMV("MinBoughtEach",13,1,1);	/*Min number of items a customer can buy*/
	MaxBoughtEach = CreateMV("MaxBoughtEach",13,1,10);	/*Max number of items a customer can buy is [MinBoughtEach+MaxBoughtEach-1]*/
	/*PriceFactor = CreateMV("PriceFactor",11,1,100);*/		/*The Factor that affects randomly generated prices*/
	/*MinPrice = CreateMV("MinPrice",8,1,0);*/			/*The Mininum price can be generated*/
	ManagerTrackLine = CreateMV("ManagerTrackLine",16,1,true);/*Does Manager keep track of Line himself*/
	
	ProgramRun=CreateMV("ProgramRun",10,1,true);
	TotalSales=CreateMV("TotalSales",10,1,0);
	CustomerBusyWaiting = CreateMV("CustomerBusyWaiting",19,1,false);
	ServiceButtonOn=CreateMV("ServiceButtonOn",15,1,0);
	ClerkCall=CreateMV("ClerkCall",9,1,0);
	ClerkCallId=CreateMV("ClerkCallId",11,1,-1);
       	StockBoyInRoom=CreateMV("StockBoyInRoom",14,1,-1);
       	CustomerLeaving=CreateMV("CustomerLeaving",15,1,0);
	
	/*For CR-related MV*/
	QueueLength=CreateMV("QueueLength",11,CRNum,0);
	ClerkOnWork=CreateMV("ClerkOnWork",11,CRNum,1);
	BornLazy=CreateMV("BornLazy",8,CRNum,0);
	TableArea=CreateMV("TableArea",9,CRNum,-1);	
	CashArea=CreateMV("CashArea",8,CRNum,0);
	data=CreateMV("data",4,CRNum,-1);			/*-1 is used to denote initial state for cash register*/
	MoneyCollected=CreateMV("MoneyCollected",14,CRNum,0);
	TotalMoneyCollected=CreateMV("TotalMoneyCollected",19,CRNum,0);
	MoneyLacked=CreateMV("MoneyLacked",11,CRNum,-1);

	/*For stockboy-related MV*/
	Booklet=CreateMV("Booklet",7,StockBoyNum,-1);	
	StockBoyStatus = CreateMV("StockBoyStatus",14,StockBoyNum,-1);/*StockBoyStatus == -1 means he is out of any aisle*/
	DidSomething=CreateMV("DidSomething",12,StockBoyNum,false);

	/*For aisle-related MV*/
	AisleNeedRestock=CreateMV("AisleNeedRestock",16,AisleNum,0);	/*no aisle need to be restocked at very beginning*/
	AisleHelpLineLength=CreateMV("AisleHelpLineLength",19,AisleNum,0);/*originally no customer is waiting for help in any aisle*/
	/*Initialization of array varibles declared*/

	/*For item-related MV*/
	Prices=CreateMV("Prices",6,ItemNum,0);
	ItemRestockPrediction=CreateMV("ItemRestockPrediction",21,ItemNum,0);/*Item to be restocked is 0	*/
	ItemAmount = CreateMV("ItemAmount",10,ItemNum,3);	/*Origin Amount of each Item*/
	
	for(i=0;i<CRNum;i++)
	{
		/*char* name;*/
		name="Queue0000";
		modifyName(name,5,i);	
		Queue[i] = RemoteCreateCondition(name,9);
		
		name="TableLock0000";
		modifyName(name,9,i);
		TableLock[i]=RemoteCreateLock(name,13);
		
		name="Table0000";
		modifyName(name,5,i);
		Table[i]=RemoteCreateCondition(name,9);
		
		name="ScreenLock0000";
		modifyName(name,10,i);
		ScreenLock[i]=RemoteCreateLock(name,14);
			
		name="Screen0000";
		modifyName(name,6,i);
		Screen[i]=RemoteCreateCondition(name,10);
		
		name="Lack0000";
		modifyName(name,4,i);
		LackMoney[i]=RemoteCreateCondition(name,8);
	}

	for(i=0;i<StockBoyNum;i++)
	{
		name="ServiceStateLock0000";
		modifyName(name,16,i);
		ServiceStateLock[i]=RemoteCreateLock(name,20);
		
		name="ServiceState0000";
		modifyName(name,12,i);
		ServiceStateCV[i]=RemoteCreateCondition(name,16);
	}

	for (i=0;i<AisleNum;i++)
	{
		name="AisleHelpLock0000";
		modifyName(name,13,i);
		AisleHelpLock[i]=RemoteCreateLock(name,17);
		
		name="AisleHelpCV0000";
		modifyName(name,11,i);
		AisleHelpCV[i]=RemoteCreateCondition(name,15);
		
		name="AisleLock0000";
		modifyName(name,9,i);
		AisleLock[i]=RemoteCreateLock(name,13);
	}
	
	for(i=0;i<ItemNum;i++)
	{
		name="WaitForRestockCV0000";
		modifyName(name,16,i);
		WaitForRestockCV[i]=RemoteCreateCondition(name,20);
	}
}


/*---------------------Clerk Minor-----------------------*/
/*Clerk Behavior*/
/*	the main Logic of a Clerk*/
/*-------------------------------------------------------*/
void
ClerkTakeTime(int time)									/*Take time to do something*/
{
	int i;
	int sum=0;
	for(i=0;i<time*100;i++)
		sum+=i;
}
	
int
CheckWaitingQueue(int id)								/*Check length of paying line*/
{
	int length =GetMV(QueueLength,id);
	return length;
}

void
CallManager(int id)										/*Clerk will cal manager if there are too many customer in line*/
{
	WriteFormat("Clerk [%d] sees more than 4 people in line and calls manager.\n", 63, &id, 1);
	RemoteAcquireLock(ClerkCallLock);
	SetMV(ClerkCall,0,true);
	SetMV(ClerkCallId,0,id);
	RemoteReleaseLock(ClerkCallLock);
}

void
ClerkRest(int id)										/*Clerk go to rest, assumes ClerkOnWork has been marked false*/
{
	int i;
	int numbers[2];
	numbers[0]=id;
	numbers[1]=id;
	WriteFormat("Clerk [%d] is leaving Cash Register [%d] and going on break.\n",62, numbers,2);
	RemoteAcquireLock(OpLock3);
	RemoteWaitCondition(ClerkOnBreak,OpLock3);						/*Wait on clerk break condition*/
	RemoteReleaseLock(OpLock3);
	
	WriteFormat("Clerk [%d] was called by Manager to work.\n", 43, numbers, 1);	/*Waken by manager to work*/
	RemoteAcquireLock(CheckQueue);
	SetMV(ClerkOnWork,id,true);								/*Reset ClerkOnWork to true*/
	RemoteReleaseLock(CheckQueue);

	ClerkTakeTime(200);										/*Wait for some time, let customer wait in line*/
		
}

void
GreetOneWaiter(int id)									/*Tell one customer in line to hand off items*/
{
	RemoteSignalCondition(Queue[id],CheckQueue);
}

int 
checkTable(int id)										/*Check what has been put in table by customer*/
{
	RemoteSignalCondition(Table[id],TableLock[id]);
	RemoteWaitCondition(Table[id],TableLock[id]);
	return GetMV(TableArea,id);
}

void
TypeInCashier(int id,int item)							/*Clerk type information in cash register*/
{
	SetMV(data,id,(int) item);
	RemoteSignalCondition(Screen[id],ScreenLock[id]);
	RemoteWaitCondition(Screen[id],ScreenLock[id]);					/*Wait for cash register's reflection*/
}

int
ReadScreen(int id)										/*Type in all item scanned, and read screen for total cost*/
{
	int total;
	TypeInCashier(id,-1);								/*According to protocol, -1 tells cash register transcation over and let it count total*/
	total = GetMV(data,id);							/*Read total*/
	return total;
}

void
ProvideReceipt(int id)
{
	RemoteSignalCondition(Table[id],TableLock[id]);
	RemoteWaitCondition(Table[id],TableLock[id]);
}


/*---------------------Clerk Major-----------------------*/
/*Clerk Behavior*/
/*	the main Logic of a Clerk*/
/*-------------------------------------------------------*/

/*Assumption:	Each Clerk works on CashRegister corresponding to his number*/
void
ClerkBehavior()
{
	int id;
	int i;
	int numbers[6];
	RemoteAcquireLock(idLock);
	id=GetMV(nextClerkID,0);
	SetMV(nextClerkID,0,id+1);
	RemoteReleaseLock(idLock);
	
	if(GetMV(BornLazy,id))									/*Used by test case, some clerk go to rest at very beginning*/
	{	
		RemoteAcquireLock(CheckQueue);
		SetMV(ClerkOnWork,id,false);
		RemoteReleaseLock(CheckQueue);
		ClerkRest(id);
	}

	ClerkTakeTime(200);										/*When just created, yield some time to make sure some customer finish shopping*/
		

	/*printf("Clerk %s: Ready to work\n",name);*/

	while(GetMV(ProgramRun,0))									/*As long as program is running*/
	{
		int people;
		int cusId;
		bool retCustomer;
		int item;
		int total;

		WriteFormat("Clerk [%d] Running...\n",23,&id,1);

		ClerkTakeTime(5);

		RemoteAcquireLock(CheckQueue);
		people = CheckWaitingQueue(id);				/*Check line length*/
		
		/*printf("Clerk %s: %2d people are waiting in line\n",name,people);*/
		
		if(people>=4)									/*If more than 4 people waiting in line, call manager*/
			CallManager(id);
		else if(people==0)								/*If no people waiting, go to rest*/
		{
			WriteFormat("Clerk [%d] Sleepy...\n",22,&id,1);
			if(!GetMV(ProgramRun,0))								/*If Program stops, Exit*/
				Exit(0);
			SetMV(ClerkOnWork,id,false);						/*Mark work status to be false*/
			RemoteReleaseLock(CheckQueue);
			ClerkRest(id);								/*go to rest*/
			continue;									/*when waken by manager, start a new work cycle*/
		}
		
		/*printf("Clerk %s: Tell one customer in line to take off items\n",name);*/
		
		GreetOneWaiter(id);								/*Tell one customer to show item, and grabing Table Lock*/
		RemoteAcquireLock(TableLock[id]);
		RemoteReleaseLock(CheckQueue);

		RemoteWaitCondition(Table[id],TableLock[id]);			/*wait for customer to come*/
		
		cusId = GetMV(TableArea,id);							/*check customer id placed on table area by customerk*/

		RemoteAcquireLock(ScreenLock[id]);

		TypeInCashier(id,cusId);						/*Type customer id in cash register*/

		retCustomer = checkTable(id);					/*check if the customer is a return customer*/
	
		/*printf("!!!!! Clerk [%d] knows Customer [%d] is [%s] a return customer!!!!\n",id, cusId, retCustomer?"":"NOT");*/
		
		TypeInCashier(id,retCustomer);					/*Type customer purpose in cash register*/

		item = checkTable(id);							/*start to scan customer item*/

		while(item!=-1)									/*As long as item!= -1, keep taking new items and type in cash register*/
		{
			numbers[0]=id;
			numbers[1]=item;
			numbers[2]=cusId;
			WriteFormat("Clerk [%d] got item [%d] from Customer [%d]\n", 45, numbers, 3);
			
			numbers[2]=id;
			WriteFormat("Clerk [%d] gave item [%d] to Cash Register [%d] for scanning.\n", 63, numbers, 3);
			
			TypeInCashier(id,item);						/*type item into cashier*/
			item = checkTable(id);						/*check another item	*/
		}
		
		/*printf("Clerk %s: Command computer to count total\n",name);*/
		
		total = ReadScreen(id);					/*Tell machine to count and read Cashier Screen for total*/
		
		numbers[0]=id;
		numbers[1]=total/10;
		numbers[2]=total%10;
		numbers[3]=id;
		WriteFormat("Clerk [%d] got total cost $[%d.%d] from Cash Register [%d]\n",63,numbers,4);
		
		numbers[1]=cusId;
		numbers[2]=total/10;
		numbers[3]=total%10;
		WriteFormat("Clerk [%d] tells Customer [%d] total cost is $[%d.%d].\n",59,numbers,4);	/*Tell Customer amount*/
	
		if(retCustomer)									/*If this is a return customer, we have to provide cash along with total value*/
		{
			RemoteSignalCondition(Screen[id],ScreenLock[id]);		/*tell cash register to prepare cash*/
			RemoteWaitCondition(Screen[id],ScreenLock[id]);		/*wait for cash to be prepared together with receipt*/
			SetMV(CashArea,id, total);						/*put money on cash area*/
			
			numbers[0]=id;
			numbers[1]=total/10;
			numbers[2]=total%10;
			numbers[3]=id;
			WriteFormat("Clerk [%d] got money $[%d.%d] from Cash Register [%d].\n", 59, numbers, 4);
			RemoteSignalCondition(Table[id],TableLock[id]);			/*tell customer to pick cash*/
			
			numbers[3]=cusId;
			WriteFormat("Clerk [%d] gave the money $[%d.%d] to Customer [%d].\n", 57, numbers,4);
			RemoteWaitCondition(Table[id],TableLock[id]);				/*Now customer has been paid*/
		}
		else											/*if it is a normal customer, collect cash from him */
		{
			SetMV(CashArea,id,total);						/*show total cost to customer*/
			RemoteSignalCondition(Table[id],TableLock[id]);			/*Tell customer to pay*/
			RemoteWaitCondition(Table[id],TableLock[id]);				/*Now customer has paid*/
			
			numbers[0]=id;
			numbers[1]=total/10;
			numbers[2]=total%10;
			numbers[3]=cusId;
			WriteFormat("Clerk [%d] got money $[%d.%d] from Customer [%d].\n",54, numbers, 4);
			RemoteSignalCondition(Screen[id],ScreenLock[id]);		/*tell chase register money has been collected*/
			
			numbers[3]=id;
			WriteFormat("Clerk [%d] gave the money $[%d.%d] to Cash Register [%d].\n", 62, numbers,4);
			RemoteWaitCondition(Screen[id],ScreenLock[id]);		/*wait for machine to take in money*/
		}
		RemoteSignalCondition(Screen[id],ScreenLock[id]);			/*tell cash register to print receipt*/
		RemoteWaitCondition(Screen[id],ScreenLock[id]);			/*wait for receipt*/
		
		numbers[0]=id;
		numbers[1]=id;
		WriteFormat("Clerk [%d] got receipt from Cash Register [%d].\n",49, numbers,2);
		RemoteReleaseLock(ScreenLock[id]);
		
		numbers[1]=cusId;
		WriteFormat("Clerk [%d] gave the receipt to Customer [%d] and tells him to leave.\n", 70, numbers, 2);
		ProvideReceipt(id);								/*provide receipt to customer, and wait for him to leave*/
		RemoteReleaseLock(TableLock[id]);
	}
	Exit(0);
}


int main()
{
	initialize();
	ClerkBehavior();
}
